use actix_web::{web::{Data, Path}, post, delete, put, get};
use actix_session::Session;
use serde::Deserialize;
use serde_json::json;
use validator::Validate;
use actix_web_validator::{Json, Query};
use sea_orm::{Set, EntityTrait, QueryFilter, ColumnTrait, PaginatorTrait, QueryOrder};

use crate::{controller::{resp, form::PagerForm, user::Auth}, model::post, util, AppState};

#[derive(Deserialize, Validate)]
pub struct PostForm {
    #[validate(length(min = 1, max = 50))]
    title: String,
    #[validate(length(min = 1, max = 500))]
    content: String
}

#[post("/post")]
pub async fn create_post(data: Data<AppState>, session: Session, form: Json<PostForm>) -> resp::Result {
    let auth = Auth::require_role(&session, Auth::LOGGED_IN)?;

    let form = form.into_inner();
    let now = util::now();

    let id = post::Entity::insert(post::ActiveModel {
        author_id: Set(auth.id),
        title: Set(form.title),
        content: Set(form.content),
        created_at: Set(now),
        updated_at: Set(now),
        ..Default::default()
    }).exec(&data.db).await?.last_insert_id;

    resp::ok(json!({ "id": id }))
}

#[delete("/post/{id}")]
pub async fn delete_post(data: Data<AppState>, session: Session, id: Path<i32>) -> resp::Result {
    let auth = Auth::require_role(&session, Auth::LOGGED_IN)?;

    let cnt = post::Entity::delete_by_id(id.into_inner())
        .filter(post::Column::AuthorId.eq(auth.id))
        .exec(&data.db).await?.rows_affected;

    if cnt == 0 {
        return resp::err("Post does not exist");
    }

    resp::ok(json!({}))
}

#[put("/post/{id}")]
pub async fn update_post(data: Data<AppState>, session: Session, id: Path<i32>, form: Json<PostForm>) -> resp::Result {
    let auth = Auth::require_role(&session, Auth::LOGGED_IN)?;

    let form = form.into_inner();

    let cnt = post::Entity::update_many()
        .set(post::ActiveModel {
            title: Set(form.title),
            content: Set(form.content),
            updated_at: Set(util::now()),
            ..Default::default()
        })
        .filter(post::Column::Id.eq(id.into_inner()))
        .filter(post::Column::AuthorId.eq(auth.id))
        .exec(&data.db).await?.rows_affected;

    if cnt == 0 {
        return resp::err("Post does not exist");
    }

    resp::ok(json!({}))
}

#[get("/post/list")]
pub async fn get_post_list(data: Data<AppState>, session: Session, form: Query<PagerForm>) -> resp::Result {
    let auth = Auth::require_role(&session, Auth::LOGGED_IN)?;

    let filter = post::Column::AuthorId.eq(auth.id);

    let total = post::Entity::find()
        .filter(filter.clone())
        .count(&data.db).await?;

    let post_list = post::Entity::find()
        .filter(filter)
        .order_by_desc(post::Column::UpdatedAt)
        .paginate(&data.db, form.limit)
        .fetch_page(form.page).await?;

    resp::ok(json!({ "total": total, "post_list": post_list }))
}
